package com.varian.boot.config;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.NumberUtil;
import com.varian.tool.Func;
import com.varian.tool.enums.BaseEnum;
import com.varian.tool.exception.ServerException;
import org.springframework.core.convert.converter.Converter;
import org.springframework.core.convert.converter.ConverterFactory;
import org.springframework.lang.Nullable;

import java.util.Map;

/**
 * StringToEnumConverterFactory
 *
 * @author ben
 * @since 2024/7/6
 */
@SuppressWarnings({"rawtypes", "unchecked"})
public class NumberStringToBaseEnumConverterFactory implements ConverterFactory<String, BaseEnum> {

    @Override
    public <T extends BaseEnum> Converter<String, T> getConverter(Class<T> targetType) {
        return new IntegerStringToBaseEnum(Func.getEnumType(targetType));
    }

    private class IntegerStringToBaseEnum<E extends BaseEnum> implements Converter<String, E> {

        private final Map<Integer, E> enumMap;
        private final Class<E> enumType;

        private IntegerStringToBaseEnum(Class<E> enumType) {
            this.enumType = enumType;
            this.enumMap = MapUtil.newHashMap(enumType.getEnumConstants().length);
            for (E enumConstant : enumType.getEnumConstants()) {
                this.enumMap.put(enumConstant.getValue(), enumConstant);
            }
        }

        @Override
        @Nullable
        public E convert(String source) {
            String trimmed = source.trim();
            Assert.isTrue(NumberUtil.isNumber(trimmed), () -> ServerException.of("Not a number: {}", source));
            Assert.isTrue(this.enumMap.containsKey(Integer.parseInt(trimmed)), () -> ServerException.of("No enum constant " + this.enumType.getCanonicalName() + "." + source));
            return this.enumMap.get(Integer.parseInt(trimmed));
        }
    }
}
